package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.feign.CartFeignClient;
import com.atguigu.gmall.cart.model.CartInfo;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.enums.model.OrderStatus;
import com.atguigu.gmall.enums.model.PaymentWay;
import com.atguigu.gmall.enums.model.ProcessStatus;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.model.OrderDetail;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.user.client.UserFeignClient;
import com.atguigu.gmall.user.model.UserAddress;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.aspectj.weaver.ast.Var;
import org.junit.jupiter.api.Order;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service
@SuppressWarnings("all")
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private RabbitService rabbitService;

    /**
     * 汇总订单确认页面需要5个参数
     * 1.${userAddressList} 用户收件地址列表
     * 2.${detailArrayList} 送货清单列表
     * 3.${totalNum} 总商品数量
     * 4.${totalAmount} 订单总金额
     * 5.${tradeNo} 方式订单重复提交流水号
     *
     * @param userId
     * @return
     */
    @Override
    public Map<String, Object> trade(String userId) {
        HashMap<String, Object> result = new HashMap<>();
        //1.远程调用用户微服务获取收货地址列表
        Long userIdLong = Long.valueOf(userId);
        List<UserAddress> userAddressList = userFeignClient.findUserAddressListByUserId(userIdLong);
        if (!CollectionUtils.isEmpty(userAddressList)) {
            result.put("userAddressList", userAddressList);
        }

        //2.远程调用购物车微服务获取选中的购物车商品-将得到CarInfo封装为OrderDetail对象
        List<CartInfo> cartCheckedList = cartFeignClient.getCartCheckedList(userIdLong);
        if (!CollectionUtils.isEmpty(cartCheckedList)) {
            List<OrderDetail> orderDetailList = cartCheckedList.stream().map(cartInfo -> {
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setSkuName(cartInfo.getSkuName());
                orderDetail.setSkuId(cartInfo.getSkuId());
                //价格 远程获取最新商品价格
                orderDetail.setOrderPrice(productFeignClient.getSkuPrice(cartInfo.getSkuId()));
                orderDetail.setImgUrl(cartInfo.getImgUrl());
                orderDetail.setSkuNum(cartInfo.getSkuNum());
                return orderDetail;
            }).collect(Collectors.toList());
            result.put("detailArrayList", orderDetailList);
            //3.总商品数量
            result.put("totalNum", cartCheckedList.size());

            //4.计算订单总金额
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderDetailList(orderDetailList);
            //调用计算总金额方法
            orderInfo.sumTotalAmount();
            result.put("totalAmount", orderInfo.getTotalAmount());
        }
        //5.防止订单重复提交生成唯一流水号
        String tradeCode = getTradeCode(userId);
        result.put("tradeNo", tradeCode);
        return result;
    }


    /**
     * 订单保存业务
     * 1.防止订单重复提交
     * 2.验证每件商品库存是否充足，商品价格是否为最新
     * 3.保存订单以及订单详情
     * 4.购物车中中结算商品删除
     * 5.返回订单ID
     *
     * @param orderInfo 订单信息
     * @param tradeNo   用户提交流水号
     * @return
     */
    @Override
    public Long submitOrder(OrderInfo orderInfo, String tradeNo) {
        //1.防止订单重复提交 如果判断+删除java代码实现 不是原子操作
        String userId = orderInfo.getUserId().toString();
        //Boolean flag = this.checkTradeCode(userId, tradeNo);
        //if (!flag) {
        //    //抛出运行时异常或者自定义异常，封装错误提示信息给前端
        //    throw new RuntimeException("请勿重复提交订单！");
        //}
        //this.deleteTradeCode(userId);
        //KEYS[1]:用户流水号Key  ARGV[1]:用户提交流水号
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        String tradeKey = "user:" + userId + ":tradecode";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        Long result = (Long) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), tradeNo);
        if (result == 0) {
            throw new RuntimeException("请勿重复提交订单！");
        }

        //2.验证每件商品库存是否充足，商品价格是否为最新
        //2.1 获取订单中订单详情中商品列表
        List<String> errorMsgList = new ArrayList<>();
        //声明集合存放异步任务
        List<CompletableFuture> futureArrayList = new ArrayList<>();
        List<OrderDetail> orderDetails = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetails)) {
            String userCartKey = "user:" + userId + ":cart";
            BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(userCartKey);
            //2.1遍历过程中判断每个商品库存以及价格是否合法
            for (OrderDetail orderDetail : orderDetails) {
                //2.2检验库存
                CompletableFuture<Void> stockCompletableFuture = CompletableFuture.runAsync(() -> {
                    String checkStockUrl = "http://localhost:9001/hasStock?skuId=" + orderDetail.getSkuId() + "&num=" + orderDetail.getSkuNum();
                    String stockResult = HttpClientUtil.doGet(checkStockUrl);
                    if ("0".equals(stockResult)) {
                        errorMsgList.add(orderDetail.getSkuName() + "库存不足");
                    }
                }, threadPoolExecutor);
                futureArrayList.add(stockCompletableFuture);


                //2.3查询价格是否为商品最新价格
                CompletableFuture<Void> priceCompletableFuture = CompletableFuture.runAsync(() -> {
                    BigDecimal skuPrice = productFeignClient.getSkuPrice(orderDetail.getSkuId());
                    if (orderDetail.getOrderPrice().compareTo(skuPrice) != 0) {
                        //价格不一致
                        errorMsgList.add(orderDetail.getSkuName() + "价格失效");
                        //2.4修改redis中用户购物车中价格改为最新
                        CartInfo cartInfo = hashOps.get(orderDetail.getSkuId().toString());
                        cartInfo.setSkuPrice(skuPrice);
                        hashOps.put(orderDetail.getSkuId().toString(), cartInfo);
                    }
                }, threadPoolExecutor);
                futureArrayList.add(priceCompletableFuture);
            }
        }
        //2.5 多个任务组合执行-并行执行
        CompletableFuture.allOf(futureArrayList.toArray(new CompletableFuture[futureArrayList.size()])).join();

        if (!CollectionUtils.isEmpty(errorMsgList)) {
            throw new RuntimeException(StringUtils.join(errorMsgList, ","));
        }

        //3.保存订单以及订单详情
        saveOrderInfo(orderInfo);

        //6.发送关闭订单延迟消息
        rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL, MqConst.ROUTING_ORDER_CANCEL, orderInfo.getId(), 45);
        return orderInfo.getId();
    }

    /**
     * 抽取出来保存订单方法
     *
     * @param orderInfo
     */
    private void saveOrderInfo(OrderInfo orderInfo) {
        List<OrderDetail> orderDetails = orderInfo.getOrderDetailList();
        //3.1 封装订单相关属性
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        orderInfo.sumTotalAmount();
        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        StringBuilder stringBuilder = new StringBuilder();
        for (OrderDetail orderDetail : orderDetails) {
            stringBuilder.append(orderDetail.getSkuName() + " ");
        }
        if (stringBuilder.toString().length() > 100) {
            orderInfo.setTradeBody(stringBuilder.toString().substring(0, 100));
        } else {
            orderInfo.setTradeBody(stringBuilder.toString());
        }
        orderInfo.setImgUrl(orderDetails.get(0).getImgUrl());
        orderInfo.setOperateTime(new Date());
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 1);
        orderInfo.setExpireTime(calendar.getTime());
        //3.2 将订单保存
        this.save(orderInfo);
        //3.3 封装订单详情对象 保存订单详情
        List<OrderDetail> orderDetailList = orderDetails.stream().map(orderDetail -> {
            orderDetail.setOrderId(orderInfo.getId());
            return orderDetail;
        }).collect(Collectors.toList());
        //3.4 批量新增订单详情
        if (!CollectionUtils.isEmpty(orderDetailList)) {
            orderDetailService.saveBatch(orderDetailList);
        }

        //TODO 4.购物车中中结算商品删除 作业
        //5.返回订单ID
    }


    /**
     * 生成流水号
     *
     * @param userId
     * @return
     */
    //@Override
    public String getTradeCode(String userId) {
        String tradeKey = "user:" + userId + ":tradecode";
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        redisTemplate.opsForValue().set(tradeKey, uuid);
        return uuid;
    }

    /**
     * 校验流水号是否一致
     *
     * @param userId
     * @param tradeCode
     * @return
     */
    //@Override
    //public Boolean checkTradeCode(String userId, String tradeCode) {
    //    String tradeKey = "user:" + userId + ":tradecode";
    //    //1.获取redis中正确的流水号
    //    String redisTradCode = (String) redisTemplate.opsForValue().get(tradeKey);
    //    //2.判断比较是否一致
    //    return tradeCode.equals(redisTradCode);
    //}

    /**
     * 删除流水号
     *
     * @param userId
     */
    //@Override
    //public void deleteTradeCode(String userId) {
    //    String tradeKey = "user:" + userId + ":tradecode";
    //    redisTemplate.delete(tradeKey);
    //}


    /**
     * 查询订单列表
     *
     * @param orderPage
     * @param userId
     * @param status
     * @return
     */
    @Override
    public IPage<Order> getOrder(IPage<Order> orderPage, String userId, String status) {
        //方式一查询动态SQL 配置一对多
        orderPage = baseMapper.getOrder(orderPage, userId, status);
        return orderPage;
    }

    /**
     * 关闭订单
     *
     * @param orderId
     */
    @Override
    public void execExpiredOrder(Long orderId) {
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);
    }

    /**
     * 修改订单为指定状态
     *
     * @param orderId
     * @param processStatus
     */
    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        //订单处理状态-工作人员
        orderInfo.setProcessStatus(processStatus.name());
        //订单状态-消费者
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        this.updateById(orderInfo);
    }

    /**
     * 根据订单ID查询订单信息
     *
     * @param orderId 订单ID
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //1.根据订单ID查询订单信息
        OrderInfo orderInfo = this.getById(orderId);
        //2.根据订单ID查询订单明细
        LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderDetail::getOrderId, orderId);
        List<OrderDetail> detailList = orderDetailService.list(queryWrapper);
        orderInfo.setOrderDetailList(detailList);
        return orderInfo;
    }


    /**
     * 发送扣减库存消息到MQ 通知 仓库系统
     * 消息格式JSON字符串包含什么数据？查询订单订单明细
     *
     * @param orderId
     */
    @Override
    public void sendDeductStockMsg(Long orderId) {
        //1.根据订单ID查询订单详情跟订单明细（商品）
        OrderInfo orderInfo = this.getOrderInfo(orderId);

        //2.将得到消息对应java对象转为Map
        Map wareMap = initWareOrder(orderInfo);

        //3.发送消息到RabbitMQ
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK, MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(wareMap));

    }


    /**
     * 将得到订单以及明细转为Map
     *
     * @param orderInfo
     * @return
     */
    @Override
    public Map initWareOrder(OrderInfo orderInfo) {
        HashMap<String, Object> mapResult = new HashMap<>();
        //从orderInfo获取封装相关订单信息
        mapResult.put("orderId", orderInfo.getId());
        mapResult.put("consignee", orderInfo.getConsignee());
        mapResult.put("consigneeTel", orderInfo.getConsigneeTel());
        mapResult.put("orderComment", orderInfo.getOrderComment());
        mapResult.put("orderBody", orderInfo.getTradeBody());
        mapResult.put("deliveryAddress", orderInfo.getDeliveryAddress());
        mapResult.put("paymentWay", "2");
        //TODO 拆单后返回对应商品仓库ID 死循环  broker投递消息 消费者永远消费失败  消费者没有应答，消息重新入队
        mapResult.put("wareId", orderInfo.getWareId());
        //从orderInfo获取订单明细 封装相关明细信息
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)) {
            List<HashMap<String, Object>> orderDetailMapList = orderDetailList.stream().map(orderDetail -> {
                HashMap<String, Object> detailMap = new HashMap<>();
                detailMap.put("skuId", orderDetail.getSkuId());
                detailMap.put("skuNum", orderDetail.getSkuNum());
                detailMap.put("skuName", orderDetail.getSkuName());
                return detailMap;
            }).collect(Collectors.toList());
            mapResult.put("details", orderDetailMapList);
        }
        return mapResult;
    }

    /**
     * 拆分订单处理
     * 1.根据提交订单ID查询原始的订单以及订单明细
     * 2.遍历仓库跟商品SKU对应关系List 构建新的子订单 订单明细 进行保存
     * 3.更新原始订单状态：SPLIT
     * 4.按照接口文档构建响应结果[{orderId:1,wareId:"出货仓库ID",orderBody:"",details:[{},{}]},{}]
     *
     * @param map
     * @return
     */
    @Override
    public String orderSplit(Long orderId, String wareSkuMapStr) {
        //保存所有的新的子订单集合
        List<OrderInfo> allSubOrderInfoList = new ArrayList<>();
        //1.根据订单ID查询原始订单以及订单明细
        OrderInfo orderInfoOrigin = this.getOrderInfo(orderId);
        //原始订单中所有订单商品明细
        List<OrderDetail> orderDetailOriginList = orderInfoOrigin.getOrderDetailList();
        //2.根据提交仓库跟SKU对应关系,构建新子订单以及订单明细并保存
        List<Map> wareSkuMap = JSON.parseArray(wareSkuMapStr, Map.class);
        if (!CollectionUtils.isEmpty(wareSkuMap)) {
            //2.1 遍历仓库商品SKU对照集合 对应产生几个子订单
            wareSkuMap.stream().forEach(skuWareMap -> {
                //获取仓库ID
                String wareId = (String) skuWareMap.get("wareId");
                //获取仓库包含SKUID集合
                List<String> skuIds = (List<String>) skuWareMap.get("skuIds");
                //使用Stream流式变成进行对集合数据过滤
                List<OrderDetail> subOrderDetailList = orderDetailOriginList.stream().filter(orderDetail -> {
                    return skuIds.contains(orderDetail.getSkuId().toString());
                }).collect(Collectors.toList());

                //2.2 构建新子订单，设置上级订单；设置订单仓库ID；设置订单ID 执行保存
                OrderInfo subOrderInfo = new OrderInfo();
                BeanUtils.copyProperties(orderInfoOrigin, subOrderInfo);
                subOrderInfo.setId(null); //避免主键冲突
                subOrderInfo.setParentOrderId(orderInfoOrigin.getId());//上级订单ID
                subOrderInfo.setWareId(wareId);// 订单对应出库仓库ID
                //重新计算新子订单总金额
                subOrderInfo.setOrderDetailList(subOrderDetailList);
                //计算总金额
                subOrderInfo.sumTotalAmount();
                //执行保存子订单
                this.save(subOrderInfo);

                //2.3 构建新子订单的订单明细，设置所属订单新子订单 执行保存
                //保存子订单项
                for (OrderDetail orderDetail : subOrderDetailList) {
                    orderDetail.setOrderId(subOrderInfo.getId());
                }
                orderDetailService.saveBatch(subOrderDetailList);
                //将创建的新子订单加入集合
                allSubOrderInfoList.add(subOrderInfo);
            });

        }
        //3.更新原始订单状态
        this.updateOrderStatus(orderInfoOrigin.getId(), ProcessStatus.SPLIT);

        //4.按照接口文档构建响应结果"[{orderId:1,wareId:"出货仓库ID",orderBody:"",details:[{},{}]},{}]"
        List<Map> collect = allSubOrderInfoList.stream().map(orderInfo -> {
            Map map = initWareOrder(orderInfo);
            return map;
        }).collect(Collectors.toList());

        return JSON.toJSONString(collect);
    }


    /**
     * 保存秒杀订单方法
     *
     * @param orderInfo
     * @return
     */
    @Override
    public Long submitSeckillOrder(OrderInfo orderInfo) {
        //保存秒杀订单
        this.saveOrderInfo(orderInfo);
        //发送延迟关闭订单消息
        rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL, MqConst.ROUTING_ORDER_CANCEL, orderInfo.getId(), 15 * 60);
        //返回保存后订单ID
        return orderInfo.getId();
    }

}
